home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tcl7.4 / tclHistory.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-04  |  29.7 KB  |  1,099 lines

  1. /* 
  2.  * tclHistory.c --
  3.  *
  4.  *    This module implements history as an optional addition to Tcl.
  5.  *    It can be called to record commands ("events") before they are
  6.  *    executed, and it provides a command that may be used to perform
  7.  *    history substitutions.
  8.  *
  9.  * Copyright (c) 1990-1993 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. #ifndef lint
  17. static char sccsid[] = "@(#) tclHistory.c 1.37 95/05/04 11:51:16";
  18. #endif /* not lint */
  19.  
  20. #include "tclInt.h"
  21. #include "tclPort.h"
  22.  
  23. /*
  24.  * This history stuff is mostly straightforward, except for one thing
  25.  * that makes everything very complicated.  Suppose that the following
  26.  * commands get executed:
  27.  *    echo foo
  28.  *    history redo
  29.  * It's important that the history event recorded for the second command
  30.  * be "echo foo", not "history redo".  Otherwise, if another "history redo"
  31.  * command is typed, it will result in infinite recursions on the
  32.  * "history redo" command.  Thus, the actual recorded history must be
  33.  *    echo foo
  34.  *    echo foo
  35.  * To do this, the history command revises recorded history as part of
  36.  * its execution.  In the example above, when "history redo" starts
  37.  * execution, the current event is "history redo", but the history
  38.  * command arranges for the current event to be changed to "echo foo".
  39.  *
  40.  * There are three additional complications.  The first is that history
  41.  * substitution may only be part of a command, as in the following
  42.  * command sequence:
  43.  *    echo foo bar
  44.  *    echo [history word 3]
  45.  * In this case, the second event should be recorded as "echo bar".  Only
  46.  * part of the recorded event is to be modified.  Fortunately, Tcl_Eval
  47.  * helps with this by recording (in the evalFirst and evalLast fields of
  48.  * the intepreter) the location of the command being executed, so the
  49.  * history module can replace exactly the range of bytes corresponding
  50.  * to the history substitution command.
  51.  *
  52.  * The second complication is that there are two ways to revise history:
  53.  * replace a command, and replace the result of a command.  Consider the
  54.  * two examples below:
  55.  *    format {result is %d} $num       |    format {result is %d} $num
  56.  *    print [history redo]           |    print [history word 3]
  57.  * Recorded history for these two cases should be as follows:
  58.  *    format {result is %d} $num       |    format {result is %d} $num
  59.  *    print [format {result is %d} $num] |    print $num
  60.  * In the left case, the history command was replaced with another command
  61.  * to be executed (the brackets were retained), but in the case on the
  62.  * right the result of executing the history command was replaced (i.e.
  63.  * brackets were replaced too).
  64.  *
  65.  * The third complication is that there could potentially be many
  66.  * history substitutions within a single command, as in:
  67.  *    echo [history word 3] [history word 2]
  68.  * There could even be nested history substitutions, as in:
  69.  *    history subs abc [history word 2]
  70.  * If history revisions were made immediately during each "history" command
  71.  * invocations, it would be very difficult to produce the correct cumulative
  72.  * effect from several substitutions in the same command.  To get around
  73.  * this problem, the actual history revision isn't made during the execution
  74.  * of the "history" command.  Information about the changes is just recorded,
  75.  * in xxx records, and the actual changes are made during the next call to
  76.  * Tcl_RecordHistory (when we know that execution of the previous command
  77.  * has finished).
  78.  */
  79.  
  80. /*
  81.  * Default space allocation for command strings:
  82.  */
  83.  
  84. #define INITIAL_CMD_SIZE 40
  85.  
  86. /*
  87.  * Forward declarations for procedures defined later in this file:
  88.  */
  89.  
  90. static void        DoRevs _ANSI_ARGS_((Interp *iPtr));
  91. static HistoryEvent *    GetEvent _ANSI_ARGS_((Interp *iPtr, char *string));
  92. static char *        GetWords _ANSI_ARGS_((Interp *iPtr, char *command,
  93.                 char *words));
  94. static void        InitHistory _ANSI_ARGS_((Interp *iPtr));
  95. static void        InsertRev _ANSI_ARGS_((Interp *iPtr,
  96.                 HistoryRev *revPtr));
  97. static void        MakeSpace _ANSI_ARGS_((HistoryEvent *hPtr, int size));
  98. static void        RevCommand _ANSI_ARGS_((Interp *iPtr, char *string));
  99. static void        RevResult _ANSI_ARGS_((Interp *iPtr, char *string));
  100. static int        SubsAndEval _ANSI_ARGS_((Interp *iPtr, char *cmd,
  101.                 char *old, char *new));
  102.  
  103. /*
  104.  *----------------------------------------------------------------------
  105.  *
  106.  * InitHistory --
  107.  *
  108.  *    Initialize history-related state in an interpreter.
  109.  *
  110.  * Results:
  111.  *    None.
  112.  *
  113.  * Side effects:
  114.  *    History info is initialized in iPtr.
  115.  *
  116.  *----------------------------------------------------------------------
  117.  */
  118.  
  119. static void
  120. InitHistory(iPtr)
  121.     register Interp *iPtr;        /* Interpreter to initialize. */
  122. {
  123.     int i;
  124.  
  125.     if (iPtr->numEvents != 0) {
  126.     return;
  127.     }
  128.     iPtr->numEvents = 20;
  129.     iPtr->events = (HistoryEvent *)
  130.         ckalloc((unsigned) (iPtr->numEvents * sizeof(HistoryEvent)));
  131.     for (i = 0; i < iPtr->numEvents; i++) {
  132.     iPtr->events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  133.     *iPtr->events[i].command = 0;
  134.     iPtr->events[i].bytesAvl = INITIAL_CMD_SIZE;
  135.     }
  136.     iPtr->curEvent = 0;
  137.     iPtr->curEventNum = 0;
  138. }
  139.  
  140. /*
  141.  *----------------------------------------------------------------------
  142.  *
  143.  * Tcl_RecordAndEval --
  144.  *
  145.  *    This procedure adds its command argument to the current list of
  146.  *    recorded events and then executes the command by calling
  147.  *    Tcl_Eval.
  148.  *
  149.  * Results:
  150.  *    The return value is a standard Tcl return value, the result of
  151.  *    executing cmd.
  152.  *
  153.  * Side effects:
  154.  *    The command is recorded and executed.  In addition, pending history
  155.  *    revisions are carried out, and information is set up to enable
  156.  *    Tcl_Eval to identify history command ranges.  This procedure also
  157.  *    initializes history information for the interpreter, if it hasn't
  158.  *    already been initialized.
  159.  *
  160.  *----------------------------------------------------------------------
  161.  */
  162.  
  163. int
  164. Tcl_RecordAndEval(interp, cmd, flags)
  165.     Tcl_Interp *interp;        /* Token for interpreter in which command
  166.                  * will be executed. */
  167.     char *cmd;            /* Command to record. */
  168.     int flags;            /* Additional flags.  TCL_NO_EVAL means
  169.                  * only record: don't execute command.
  170.                  * TCL_EVAL_GLOBAL means use Tcl_GlobalEval
  171.                  * instead of Tcl_Eval. */
  172. {
  173.     register Interp *iPtr = (Interp *) interp;
  174.     register HistoryEvent *eventPtr;
  175.     int length, result;
  176.  
  177.     if (iPtr->numEvents == 0) {
  178.     InitHistory(iPtr);
  179.     }
  180.     DoRevs(iPtr);
  181.  
  182.     /*
  183.      * Don't record empty commands.
  184.      */
  185.  
  186.     while (isspace(UCHAR(*cmd))) {
  187.     cmd++;
  188.     }
  189.     if (*cmd == '\0') {
  190.     Tcl_ResetResult(interp);
  191.     return TCL_OK;
  192.     }
  193.  
  194.     iPtr->curEventNum++;
  195.     iPtr->curEvent++;
  196.     if (iPtr->curEvent >= iPtr->numEvents) {
  197.     iPtr->curEvent = 0;
  198.     }
  199.     eventPtr = &iPtr->events[iPtr->curEvent];
  200.  
  201.     /*
  202.      * Chop off trailing newlines before recording the command.
  203.      */
  204.  
  205.     length = strlen(cmd);
  206.     while (cmd[length-1] == '\n') {
  207.     length--;
  208.     }
  209.     MakeSpace(eventPtr, length + 1);
  210.     strncpy(eventPtr->command, cmd, (size_t) length);
  211.     eventPtr->command[length] = 0;
  212.  
  213.     /*
  214.      * Execute the command.  Note: history revision isn't possible after
  215.      * a nested call to this procedure, because the event at the top of
  216.      * the history list no longer corresponds to what's going on when
  217.      * a nested call here returns.  Thus, must leave history revision
  218.      * disabled when we return.
  219.      */
  220.  
  221.     result = TCL_OK;
  222.     if (!(flags & TCL_NO_EVAL)) {
  223.     iPtr->historyFirst = cmd;
  224.     iPtr->revDisables = 0;
  225.     iPtr->evalFlags = (flags & ~TCL_EVAL_GLOBAL) | TCL_RECORD_BOUNDS;
  226.     if (flags & TCL_EVAL_GLOBAL) {
  227.         result = Tcl_GlobalEval(interp, cmd);
  228.     } else {
  229.         result = Tcl_Eval(interp, cmd);
  230.     }
  231.     }
  232.     iPtr->revDisables = 1;
  233.     return result;
  234. }
  235.  
  236. /*
  237.  *----------------------------------------------------------------------
  238.  *
  239.  * Tcl_HistoryCmd --
  240.  *
  241.  *    This procedure is invoked to process the "history" Tcl command.
  242.  *    See the user documentation for details on what it does.
  243.  *
  244.  * Results:
  245.  *    A standard Tcl result.
  246.  *
  247.  * Side effects:
  248.  *    See the user documentation.
  249.  *
  250.  *----------------------------------------------------------------------
  251.  */
  252.  
  253.     /* ARGSUSED */
  254. int
  255. Tcl_HistoryCmd(dummy, interp, argc, argv)
  256.     ClientData dummy;            /* Not used. */
  257.     Tcl_Interp *interp;            /* Current interpreter. */
  258.     int argc;                /* Number of arguments. */
  259.     char **argv;            /* Argument strings. */
  260. {
  261.     register Interp *iPtr = (Interp *) interp;
  262.     register HistoryEvent *eventPtr;
  263.     size_t length;
  264.     int c;
  265.  
  266.     if (iPtr->numEvents == 0) {
  267.     InitHistory(iPtr);
  268.     }
  269.  
  270.     /*
  271.      * If no arguments, treat the same as "history info".
  272.      */
  273.  
  274.     if (argc == 1) {
  275.     goto infoCmd;
  276.     }
  277.  
  278.     c = argv[1][0];
  279.     length = strlen(argv[1]);
  280.  
  281.     if ((c == 'a') && (strncmp(argv[1], "add", length)) == 0) {
  282.     if ((argc != 3) && (argc != 4)) {
  283.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  284.             " add event ?exec?\"", (char *) NULL);
  285.         return TCL_ERROR;
  286.     }
  287.     if (argc == 4) {
  288.         if (strncmp(argv[3], "exec", strlen(argv[3])) != 0) {
  289.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  290.             "\": should be \"exec\"", (char *) NULL);
  291.         return TCL_ERROR;
  292.         }
  293.         return Tcl_RecordAndEval(interp, argv[2], 0);
  294.     }
  295.     return Tcl_RecordAndEval(interp, argv[2], TCL_NO_EVAL);
  296.     } else if ((c == 'c') && (strncmp(argv[1], "change", length)) == 0) {
  297.     if ((argc != 3) && (argc != 4)) {
  298.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  299.             " change newValue ?event?\"", (char *) NULL);
  300.         return TCL_ERROR;
  301.     }
  302.     if (argc == 3) {
  303.         eventPtr = &iPtr->events[iPtr->curEvent];
  304.         iPtr->revDisables += 1;
  305.         while (iPtr->revPtr != NULL) {
  306.         HistoryRev *nextPtr;
  307.  
  308.         ckfree(iPtr->revPtr->newBytes);
  309.         nextPtr = iPtr->revPtr->nextPtr;
  310.         ckfree((char *) iPtr->revPtr);
  311.         iPtr->revPtr = nextPtr;
  312.         }
  313.     } else {
  314.         eventPtr = GetEvent(iPtr, argv[3]);
  315.         if (eventPtr == NULL) {
  316.         return TCL_ERROR;
  317.         }
  318.     }
  319.     MakeSpace(eventPtr, (int) strlen(argv[2]) + 1);
  320.     strcpy(eventPtr->command, argv[2]);
  321.     return TCL_OK;
  322.     } else if ((c == 'e') && (strncmp(argv[1], "event", length)) == 0) {
  323.     if (argc > 3) {
  324.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  325.             " event ?event?\"", (char *) NULL);
  326.         return TCL_ERROR;
  327.     }
  328.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  329.     if (eventPtr == NULL) {
  330.         return TCL_ERROR;
  331.     }
  332.     RevResult(iPtr, eventPtr->command);
  333.     Tcl_SetResult(interp, eventPtr->command, TCL_VOLATILE);
  334.     return TCL_OK;
  335.     } else if ((c == 'i') && (strncmp(argv[1], "info", length)) == 0) {
  336.     int count, indx, i;
  337.     char *newline;
  338.  
  339.     if ((argc != 2) && (argc != 3)) {
  340.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  341.             " info ?count?\"", (char *) NULL);
  342.         return TCL_ERROR;
  343.     }
  344.     infoCmd:
  345.     if (argc == 3) {
  346.         if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  347.         return TCL_ERROR;
  348.         }
  349.         if (count > iPtr->numEvents) {
  350.         count = iPtr->numEvents;
  351.         }
  352.     } else {
  353.         count = iPtr->numEvents;
  354.     }
  355.     newline = "";
  356.     for (i = 0, indx = iPtr->curEvent + 1 + iPtr->numEvents - count;
  357.         i < count; i++, indx++) {
  358.         char *cur, *next, savedChar;
  359.         char serial[20];
  360.  
  361.         if (indx >= iPtr->numEvents) {
  362.         indx -= iPtr->numEvents;
  363.         }
  364.         cur = iPtr->events[indx].command;
  365.         if (*cur == '\0') {
  366.         continue;        /* No command recorded here. */
  367.         }
  368.         sprintf(serial, "%6d  ", iPtr->curEventNum + 1 - (count - i));
  369.         Tcl_AppendResult(interp, newline, serial, (char *) NULL);
  370.         newline = "\n";
  371.  
  372.         /*
  373.          * Tricky formatting here:  for multi-line commands, indent
  374.          * the continuation lines.
  375.          */
  376.  
  377.         while (1) {
  378.         next = strchr(cur, '\n');
  379.         if (next == NULL) {
  380.             break;
  381.         }
  382.         next++;
  383.         savedChar = *next;
  384.         *next = 0;
  385.         Tcl_AppendResult(interp, cur, "\t", (char *) NULL);
  386.         *next = savedChar;
  387.         cur = next;
  388.         }
  389.         Tcl_AppendResult(interp, cur, (char *) NULL);
  390.     }
  391.     return TCL_OK;
  392.     } else if ((c == 'k') && (strncmp(argv[1], "keep", length)) == 0) {
  393.     int count, i, src;
  394.     HistoryEvent *events;
  395.  
  396.     if (argc != 3) {
  397.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  398.             " keep number\"", (char *) NULL);
  399.         return TCL_ERROR;
  400.     }
  401.     if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  402.         return TCL_ERROR;
  403.     }
  404.     if ((count <= 0) || (count > 1000)) {
  405.         Tcl_AppendResult(interp, "illegal keep count \"", argv[2],
  406.             "\"", (char *) NULL);
  407.         return TCL_ERROR;
  408.     }
  409.  
  410.     /*
  411.      * Create a new history array and copy as much existing history
  412.      * as possible from the old array.
  413.      */
  414.  
  415.     events = (HistoryEvent *)
  416.         ckalloc((unsigned) (count * sizeof(HistoryEvent)));
  417.     if (count < iPtr->numEvents) {
  418.         src = iPtr->curEvent + 1 - count;
  419.         if (src < 0) {
  420.         src += iPtr->numEvents;
  421.         }
  422.     } else {
  423.         src = iPtr->curEvent + 1;
  424.     }
  425.     for (i = 0; i < count; i++, src++) {
  426.         if (src >= iPtr->numEvents) {
  427.         src = 0;
  428.         }
  429.         if (i < iPtr->numEvents) {
  430.         events[i] = iPtr->events[src];
  431.         iPtr->events[src].command = NULL;
  432.         } else {
  433.         events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  434.         events[i].command[0] = 0;
  435.         events[i].bytesAvl = INITIAL_CMD_SIZE;
  436.         }
  437.     }
  438.  
  439.     /*
  440.      * Throw away everything left in the old history array, and
  441.      * substitute the new one for the old one.
  442.      */
  443.  
  444.     for (i = 0; i < iPtr->numEvents; i++) {
  445.         if (iPtr->events[i].command != NULL) {
  446.         ckfree(iPtr->events[i].command);
  447.         }
  448.     }
  449.     ckfree((char *) iPtr->events);
  450.     iPtr->events = events;
  451.     if (count < iPtr->numEvents) {
  452.         iPtr->curEvent = count-1;
  453.     } else {
  454.         iPtr->curEvent = iPtr->numEvents-1;
  455.     }
  456.     iPtr->numEvents = count;
  457.     return TCL_OK;
  458.     } else if ((c == 'n') && (strncmp(argv[1], "nextid", length)) == 0) {
  459.     if (argc != 2) {
  460.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  461.             " nextid\"", (char *) NULL);
  462.         return TCL_ERROR;
  463.     }
  464.     sprintf(iPtr->result, "%d", iPtr->curEventNum+1);
  465.     return TCL_OK;
  466.     } else if ((c == 'r') && (strncmp(argv[1], "redo", length)) == 0) {
  467.     if (argc > 3) {
  468.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  469.             " redo ?event?\"", (char *) NULL);
  470.         return TCL_ERROR;
  471.     }
  472.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  473.     if (eventPtr == NULL) {
  474.         return TCL_ERROR;
  475.     }
  476.     RevCommand(iPtr, eventPtr->command);
  477.     return Tcl_Eval(interp, eventPtr->command);
  478.     } else if ((c == 's') && (strncmp(argv[1], "substitute", length)) == 0) {
  479.     if ((argc > 5) || (argc < 4)) {
  480.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  481.             " substitute old new ?event?\"", (char *) NULL);
  482.         return TCL_ERROR;
  483.     }
  484.     eventPtr = GetEvent(iPtr, argc==4 ? "-1" : argv[4]);
  485.     if (eventPtr == NULL) {
  486.         return TCL_ERROR;
  487.     }
  488.     return SubsAndEval(iPtr, eventPtr->command, argv[2], argv[3]);
  489.     } else if ((c == 'w') && (strncmp(argv[1], "words", length)) == 0) {
  490.     char *words;
  491.  
  492.     if ((argc != 3) && (argc != 4)) {
  493.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  494.             " words num-num/pat ?event?\"", (char *) NULL);
  495.         return TCL_ERROR;
  496.     }
  497.     eventPtr = GetEvent(iPtr, argc==3 ? "-1" : argv[3]);
  498.     if (eventPtr == NULL) {
  499.         return TCL_ERROR;
  500.     }
  501.     words = GetWords(iPtr, eventPtr->command, argv[2]);
  502.     if (words == NULL) {
  503.         return TCL_ERROR;
  504.     }
  505.     RevResult(iPtr, words);
  506.     iPtr->result = words;
  507.     iPtr->freeProc = (Tcl_FreeProc *) free;
  508.     return TCL_OK;
  509.     }
  510.  
  511.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  512.         "\": must be add, change, event, info, keep, nextid, ",
  513.         "redo, substitute, or words", (char *) NULL);
  514.     return TCL_ERROR;
  515. }
  516.  
  517. /*
  518.  *----------------------------------------------------------------------
  519.  *
  520.  * MakeSpace --
  521.  *
  522.  *    Given a history event, make sure it has enough space for
  523.  *    a string of a given length (enlarge the string area if
  524.  *    necessary).
  525.  *
  526.  * Results:
  527.  *    None.
  528.  *
  529.  * Side effects:
  530.  *    More memory may get allocated.
  531.  *
  532.  *----------------------------------------------------------------------
  533.  */
  534.  
  535. static void
  536. MakeSpace(hPtr, size)
  537.     HistoryEvent *hPtr;
  538.     int size;            /* # of bytes needed in hPtr. */
  539. {
  540.     if (hPtr->bytesAvl < size) {
  541.     ckfree(hPtr->command);
  542.     hPtr->command = (char *) ckalloc((unsigned) size);
  543.     hPtr->bytesAvl = size;
  544.     }
  545. }
  546.  
  547. /*
  548.  *----------------------------------------------------------------------
  549.  *
  550.  * InsertRev --
  551.  *
  552.  *    Add a new revision to the list of those pending for iPtr.
  553.  *    Do it in a way that keeps the revision list sorted in
  554.  *    increasing order of firstIndex.  Also, eliminate revisions
  555.  *    that are subsets of other revisions.
  556.  *
  557.  * Results:
  558.  *    None.
  559.  *
  560.  * Side effects:
  561.  *    RevPtr is added to iPtr's revision list.
  562.  *
  563.  *----------------------------------------------------------------------
  564.  */
  565.  
  566. static void
  567. InsertRev(iPtr, revPtr)
  568.     Interp *iPtr;            /* Interpreter to use. */
  569.     register HistoryRev *revPtr;    /* Revision to add to iPtr's list. */
  570. {
  571.     register HistoryRev *curPtr;
  572.     register HistoryRev *prevPtr;
  573.  
  574.     for (curPtr = iPtr->revPtr, prevPtr = NULL; curPtr != NULL;
  575.         prevPtr = curPtr, curPtr = curPtr->nextPtr) {
  576.     /*
  577.      * If this revision includes the new one (or vice versa) then
  578.      * just eliminate the one that is a subset of the other.
  579.      */
  580.  
  581.     if ((revPtr->firstIndex <= curPtr->firstIndex)
  582.         && (revPtr->lastIndex >= curPtr->firstIndex)) {
  583.         curPtr->firstIndex = revPtr->firstIndex;
  584.         curPtr->lastIndex = revPtr->lastIndex;
  585.         curPtr->newSize = revPtr->newSize;
  586.         ckfree(curPtr->newBytes);
  587.         curPtr->newBytes = revPtr->newBytes;
  588.         ckfree((char *) revPtr);
  589.         return;
  590.     }
  591.     if ((revPtr->firstIndex >= curPtr->firstIndex)
  592.         && (revPtr->lastIndex <= curPtr->lastIndex)) {
  593.         ckfree(revPtr->newBytes);
  594.         ckfree((char *) revPtr);
  595.         return;
  596.     }
  597.  
  598.     if (revPtr->firstIndex < curPtr->firstIndex) {
  599.         break;
  600.     }
  601.     }
  602.  
  603.     /*
  604.      * Insert revPtr just after prevPtr.
  605.      */
  606.  
  607.     if (prevPtr == NULL) {
  608.     revPtr->nextPtr = iPtr->revPtr;
  609.     iPtr->revPtr = revPtr;
  610.     } else {
  611.     revPtr->nextPtr = prevPtr->nextPtr;
  612.     prevPtr->nextPtr = revPtr;
  613.     }
  614. }
  615.  
  616. /*
  617.  *----------------------------------------------------------------------
  618.  *
  619.  * RevCommand --
  620.  *
  621.  *    This procedure is invoked by the "history" command to record
  622.  *    a command revision.  See the comments at the beginning of the
  623.  *    file for more information about revisions.
  624.  *
  625.  * Results:
  626.  *    None.
  627.  *
  628.  * Side effects:
  629.  *    Revision information is recorded.
  630.  *
  631.  *----------------------------------------------------------------------
  632.  */
  633.  
  634. static void
  635. RevCommand(iPtr, string)
  636.     register Interp *iPtr;    /* Interpreter in which to perform the
  637.                  * substitution. */
  638.     char *string;        /* String to substitute. */
  639. {
  640.     register HistoryRev *revPtr;
  641.  
  642.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  643.     return;
  644.     }
  645.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  646.     revPtr->firstIndex = iPtr->evalFirst - iPtr->historyFirst;
  647.     revPtr->lastIndex = iPtr->evalLast - iPtr->historyFirst;
  648.     revPtr->newSize = strlen(string);
  649.     revPtr->newBytes = (char *) ckalloc((unsigned) (revPtr->newSize+1));
  650.     strcpy(revPtr->newBytes, string);
  651.     InsertRev(iPtr, revPtr);
  652. }
  653.  
  654. /*
  655.  *----------------------------------------------------------------------
  656.  *
  657.  * RevResult --
  658.  *
  659.  *    This procedure is invoked by the "history" command to record
  660.  *    a result revision.  See the comments at the beginning of the
  661.  *    file for more information about revisions.
  662.  *
  663.  * Results:
  664.  *    None.
  665.  *
  666.  * Side effects:
  667.  *    Revision information is recorded.
  668.  *
  669.  *----------------------------------------------------------------------
  670.  */
  671.  
  672. static void
  673. RevResult(iPtr, string)
  674.     register Interp *iPtr;    /* Interpreter in which to perform the
  675.                  * substitution. */
  676.     char *string;        /* String to substitute. */
  677. {
  678.     register HistoryRev *revPtr;
  679.     char *evalFirst, *evalLast;
  680.     char *argv[2];
  681.  
  682.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  683.     return;
  684.     }
  685.  
  686.     /*
  687.      * Expand the replacement range to include the brackets that surround
  688.      * the command.  If there aren't any brackets (i.e. this command was
  689.      * invoked at top-level) then don't do any revision.  Also, if there
  690.      * are several commands in brackets, of which this is just one,
  691.      * then don't do any revision.
  692.      */
  693.  
  694.     evalFirst = iPtr->evalFirst;
  695.     evalLast = iPtr->evalLast + 1;
  696.     while (1) {
  697.     if (evalFirst == iPtr->historyFirst) {
  698.         return;
  699.     }
  700.     evalFirst--;
  701.     if (*evalFirst == '[') {
  702.         break;
  703.     }
  704.     if (!isspace(UCHAR(*evalFirst))) {
  705.         return;
  706.     }
  707.     }
  708.     if (*evalLast != ']') {
  709.     return;
  710.     }
  711.  
  712.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  713.     revPtr->firstIndex = evalFirst - iPtr->historyFirst;
  714.     revPtr->lastIndex = evalLast - iPtr->historyFirst;
  715.     argv[0] = string;
  716.     revPtr->newBytes = Tcl_Merge(1, argv);
  717.     revPtr->newSize = strlen(revPtr->newBytes);
  718.     InsertRev(iPtr, revPtr);
  719. }
  720.  
  721. /*
  722.  *----------------------------------------------------------------------
  723.  *
  724.  * DoRevs --
  725.  *
  726.  *    This procedure is called to apply the history revisions that
  727.  *    have been recorded in iPtr.
  728.  *
  729.  * Results:
  730.  *    None.
  731.  *
  732.  * Side effects:
  733.  *    The most recent entry in the history for iPtr may be modified.
  734.  *
  735.  *----------------------------------------------------------------------
  736.  */
  737.  
  738. static void
  739. DoRevs(iPtr)
  740.     register Interp *iPtr;    /* Interpreter whose history is to
  741.                  * be modified. */
  742. {
  743.     register HistoryRev *revPtr;
  744.     register HistoryEvent *eventPtr;
  745.     char *newCommand, *p;
  746.     unsigned int size;
  747.     int bytesSeen, count;
  748.  
  749.     if (iPtr->revPtr == NULL) {
  750.     return;
  751.     }
  752.  
  753.     /*
  754.      * The revision is done in two passes.  The first pass computes the
  755.      * amount of space needed for the revised event, and the second pass
  756.      * pieces together the new event and frees up the revisions.
  757.      */
  758.  
  759.     eventPtr = &iPtr->events[iPtr->curEvent];
  760.     size = strlen(eventPtr->command) + 1;
  761.     for (revPtr = iPtr->revPtr; revPtr != NULL; revPtr = revPtr->nextPtr) {
  762.     size -= revPtr->lastIndex + 1 - revPtr->firstIndex;
  763.     size += revPtr->newSize;
  764.     }
  765.  
  766.     newCommand = (char *) ckalloc(size);
  767.     p = newCommand;
  768.     bytesSeen = 0;
  769.     for (revPtr = iPtr->revPtr; revPtr != NULL; ) {
  770.     HistoryRev *nextPtr = revPtr->nextPtr;
  771.  
  772.     count = revPtr->firstIndex - bytesSeen;
  773.     if (count > 0) {
  774.         strncpy(p, eventPtr->command + bytesSeen, (size_t) count);
  775.         p += count;
  776.     }
  777.     strncpy(p, revPtr->newBytes, (size_t) revPtr->newSize);
  778.     p += revPtr->newSize;
  779.     bytesSeen = revPtr->lastIndex+1;
  780.     ckfree(revPtr->newBytes);
  781.     ckfree((char *) revPtr);
  782.     revPtr = nextPtr;
  783.     }
  784.     strcpy(p, eventPtr->command + bytesSeen);
  785.  
  786.     /*
  787.      * Replace the command in the event.
  788.      */
  789.  
  790.     ckfree(eventPtr->command);
  791.     eventPtr->command = newCommand;
  792.     eventPtr->bytesAvl = size;
  793.     iPtr->revPtr = NULL;
  794. }
  795.  
  796. /*
  797.  *----------------------------------------------------------------------
  798.  *
  799.  * GetEvent --
  800.  *
  801.  *    Given a textual description of an event (see the manual page
  802.  *    for legal values) find the corresponding event and return its
  803.  *    command string.
  804.  *
  805.  * Results:
  806.  *    The return value is a pointer to the event named by "string".
  807.  *    If no such event exists, then NULL is returned and an error
  808.  *    message is left in iPtr.
  809.  *
  810.  * Side effects:
  811.  *    None.
  812.  *
  813.  *----------------------------------------------------------------------
  814.  */
  815.  
  816. static HistoryEvent *
  817. GetEvent(iPtr, string)
  818.     register Interp *iPtr;    /* Interpreter in which to look. */
  819.     char *string;        /* Description of event. */
  820. {
  821.     int eventNum, index;
  822.     register HistoryEvent *eventPtr;
  823.     int length;
  824.  
  825.     /*
  826.      * First check for a numeric specification of an event.
  827.      */
  828.  
  829.     if (isdigit(UCHAR(*string)) || (*string == '-')) {
  830.     if (Tcl_GetInt((Tcl_Interp *) iPtr, string, &eventNum) != TCL_OK) {
  831.         return NULL;
  832.     }
  833.     if (eventNum < 0) {
  834.         eventNum += iPtr->curEventNum;
  835.         }
  836.     if (eventNum > iPtr->curEventNum) {
  837.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  838.             "\" hasn't occurred yet", (char *) NULL);
  839.         return NULL;
  840.     }
  841.     if ((eventNum <= iPtr->curEventNum-iPtr->numEvents)
  842.         || (eventNum <= 0)) {
  843.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  844.             "\" is too far in the past", (char *) NULL);
  845.         return NULL;
  846.     }
  847.     index = iPtr->curEvent + (eventNum - iPtr->curEventNum);
  848.     if (index < 0) {
  849.         index += iPtr->numEvents;
  850.     }
  851.     return &iPtr->events[index];
  852.     }
  853.  
  854.     /*
  855.      * Next, check for an event that contains the string as a prefix or
  856.      * that matches the string in the sense of Tcl_StringMatch.
  857.      */
  858.  
  859.     length = strlen(string);
  860.     for (index = iPtr->curEvent - 1; ; index--) {
  861.     if (index < 0) {
  862.         index += iPtr->numEvents;
  863.     }
  864.     if (index == iPtr->curEvent) {
  865.         break;
  866.     }
  867.     eventPtr = &iPtr->events[index];
  868.     if ((strncmp(eventPtr->command, string, (size_t) length) == 0)
  869.         || Tcl_StringMatch(eventPtr->command, string)) {
  870.         return eventPtr;
  871.     }
  872.     }
  873.  
  874.     Tcl_AppendResult((Tcl_Interp *) iPtr, "no event matches \"", string,
  875.         "\"", (char *) NULL);
  876.     return NULL;
  877. }
  878.  
  879. /*
  880.  *----------------------------------------------------------------------
  881.  *
  882.  * SubsAndEval --
  883.  *
  884.  *    Generate a new command by making a textual substitution in
  885.  *    the "cmd" argument.  Then execute the new command.
  886.  *
  887.  * Results:
  888.  *    The return value is a standard Tcl error.
  889.  *
  890.  * Side effects:
  891.  *    History gets revised if the substitution is occurring on
  892.  *    a recorded command line.  Also, the re-executed command
  893.  *    may produce side-effects.
  894.  *
  895.  *----------------------------------------------------------------------
  896.  */
  897.  
  898. static int
  899. SubsAndEval(iPtr, cmd, old, new)
  900.     register Interp *iPtr;    /* Interpreter in which to execute
  901.                  * new command. */
  902.     char *cmd;            /* Command in which to substitute. */
  903.     char *old;            /* String to search for in command. */
  904.     char *new;            /* Replacement string for "old". */
  905. {
  906.     char *src, *dst, *newCmd;
  907.     int count, oldLength, newLength, length, result;
  908.  
  909.     /*
  910.      * Figure out how much space it will take to hold the
  911.      * substituted command (and complain if the old string
  912.      * doesn't appear in the original command).
  913.      */
  914.  
  915.     oldLength = strlen(old);
  916.     newLength = strlen(new);
  917.     src = cmd;
  918.     count = 0;
  919.     while (1) {
  920.     src = strstr(src, old);
  921.     if (src == NULL) {
  922.         break;
  923.     }
  924.     src += oldLength;
  925.     count++;
  926.     }
  927.     if (count == 0) {
  928.     Tcl_AppendResult((Tcl_Interp *) iPtr, "\"", old,
  929.         "\" doesn't appear in event", (char *) NULL);
  930.     return TCL_ERROR;
  931.     }
  932.     length = strlen(cmd) + count*(newLength - oldLength);
  933.  
  934.     /*
  935.      * Generate a substituted command.
  936.      */
  937.  
  938.     newCmd = (char *) ckalloc((unsigned) (length + 1));
  939.     dst = newCmd;
  940.     while (1) {
  941.     src = strstr(cmd, old);
  942.     if (src == NULL) {
  943.         strcpy(dst, cmd);
  944.         break;
  945.     }
  946.     strncpy(dst, cmd, (size_t) (src-cmd));
  947.     dst += src-cmd;
  948.     strcpy(dst, new);
  949.     dst += newLength;
  950.     cmd = src + oldLength;
  951.     }
  952.  
  953.     RevCommand(iPtr, newCmd);
  954.     result = Tcl_Eval((Tcl_Interp *) iPtr, newCmd);
  955.     ckfree(newCmd);
  956.     return result;
  957. }
  958.  
  959. /*
  960.  *----------------------------------------------------------------------
  961.  *
  962.  * GetWords --
  963.  *
  964.  *    Given a command string, return one or more words from the
  965.  *    command string.
  966.  *
  967.  * Results:
  968.  *    The return value is a pointer to a dynamically-allocated
  969.  *    string containing the words of command specified by "words".
  970.  *    If the word specifier has improper syntax then an error
  971.  *    message is placed in iPtr->result and NULL is returned.
  972.  *
  973.  * Side effects:
  974.  *    Memory is allocated.  It is the caller's responsibilty to
  975.  *    free the returned string..
  976.  *
  977.  *----------------------------------------------------------------------
  978.  */
  979.  
  980. static char *
  981. GetWords(iPtr, command, words)
  982.     register Interp *iPtr;    /* Tcl interpreter in which to place
  983.                  * an error message if needed. */
  984.     char *command;        /* Command string. */
  985.     char *words;        /* Description of which words to extract
  986.                  * from the command.  Either num[-num] or
  987.                  * a pattern. */
  988. {
  989.     char *result;
  990.     char *start, *end, *dst;
  991.     register char *next;
  992.     int first;            /* First word desired. -1 means last word
  993.                  * only. */
  994.     int last;            /* Last word desired.  -1 means use everything
  995.                  * up to the end. */
  996.     int index;            /* Index of current word. */
  997.     char *pattern;
  998.  
  999.     /*
  1000.      * Figure out whether we're looking for a numerical range or for
  1001.      * a pattern.
  1002.      */
  1003.  
  1004.     pattern = NULL;
  1005.     first = 0;
  1006.     last = -1;
  1007.     if (*words == '$') {
  1008.     if (words[1] != '\0') {
  1009.         goto error;
  1010.     }
  1011.     first = -1;
  1012.     } else if (isdigit(UCHAR(*words))) {
  1013.     first = strtoul(words, &start, 0);
  1014.     if (*start == 0) {
  1015.         last = first;
  1016.     } else if (*start == '-') {
  1017.         start++;
  1018.         if (*start == '$') {
  1019.         start++;
  1020.         } else if (isdigit(UCHAR(*start))) {
  1021.         last = strtoul(start, &start, 0);
  1022.         } else {
  1023.         goto error;
  1024.         }
  1025.         if (*start != 0) {
  1026.         goto error;
  1027.         }
  1028.     }
  1029.     if ((first > last) && (last != -1)) {
  1030.         goto error;
  1031.     }
  1032.     } else {
  1033.     pattern = words;
  1034.     }
  1035.  
  1036.     /*
  1037.      * Scan through the words one at a time, copying those that are
  1038.      * relevant into the result string.  Allocate a result area large
  1039.      * enough to hold all the words if necessary.
  1040.      */
  1041.  
  1042.     result = (char *) ckalloc((unsigned) (strlen(command) + 1));
  1043.     dst = result;
  1044.     for (next = command; isspace(UCHAR(*next)); next++) {
  1045.     /* Empty loop body:  just find start of first word. */
  1046.     }
  1047.     for (index = 0; *next != 0; index++) {
  1048.     start = next;
  1049.     end = TclWordEnd(next, 0, (int *) NULL);
  1050.     if (*end != 0) {
  1051.         end++;
  1052.         for (next = end; isspace(UCHAR(*next)); next++) {
  1053.         /* Empty loop body:  just find start of next word. */
  1054.         }
  1055.     }
  1056.     if ((first > index) || ((first == -1) && (*next != 0))) {
  1057.         continue;
  1058.     }
  1059.     if ((last != -1) && (last < index)) {
  1060.         continue;
  1061.     }
  1062.     if (pattern != NULL) {
  1063.         int match;
  1064.         char savedChar = *end;
  1065.  
  1066.         *end = 0;
  1067.         match = Tcl_StringMatch(start, pattern);
  1068.         *end = savedChar;
  1069.         if (!match) {
  1070.         continue;
  1071.         }
  1072.     }
  1073.     if (dst != result) {
  1074.         *dst = ' ';
  1075.         dst++;
  1076.     }
  1077.     strncpy(dst, start, (size_t) (end-start));
  1078.     dst += end-start;
  1079.     }
  1080.     *dst = 0;
  1081.  
  1082.     /*
  1083.      * Check for an out-of-range argument index.
  1084.      */
  1085.  
  1086.     if ((last >= index) || (first >= index)) {
  1087.     ckfree(result);
  1088.     Tcl_AppendResult((Tcl_Interp *) iPtr, "word selector \"", words,
  1089.         "\" specified non-existent words", (char *) NULL);
  1090.     return NULL;
  1091.     }
  1092.     return result;
  1093.  
  1094.     error:
  1095.     Tcl_AppendResult((Tcl_Interp *) iPtr, "bad word selector \"", words,
  1096.         "\":  should be num-num or pattern", (char *) NULL);
  1097.     return NULL;
  1098. }
  1099.